<?php

// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614 <weibo.com/luofei614>
// +----------------------------------------------------------------------
// | 修改者: anuo (本权限类在原3.2.3的基础上修改过来的)
// +----------------------------------------------------------------------

namespace fast;

use think\Cache;
use think\Db;
use think\Config;
use think\Env;
use think\Exception;
use think\Log;
use think\Session;
use think\Request;

/**
 * nuonuo 发票
 */
class Nuo
{

    /**
     * @var object 对象实例
     */
    protected static $instance;

    /**
     * 当前请求实例
     * @var Request
     */
    protected $accessToken='';
    /**
     * SDK版本号
     * @var string
     */
    protected $VERSION = "2.0.0";
    protected $Taxnum = '';
    protected $APPKey = "";
    protected $APPSecret = "";

    protected $AUTH_URL = "https://open.nuonuo.com/accessToken";
    protected $URL = "https://sdk.nuonuo.com/open/v1/services";
    protected $header = [];


    public function __construct()
    {
        $this->Taxnum = Env::get('nuonuo.Taxnum','');
        $this->APPKey = Env::get('nuonuo.APPKey');
        $this->APPSecret = Env::get('nuonuo.APPSecret');
        $this->accessToken = Env::get('nuonuo.ACCESSTOKEN');
        if (empty($this->accessToken)){
            self::getMerchantToken();
        }
    }


    /**
     * 初始化
     * @access public
     * @param array $options 参数
     * @return Auth
     */
    public static function instance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($options);
        }
        return self::$instance;
    }

    /**
     * 商家应用获取accessToken
     *
     * 返回报文:
     * {"access_token":"xxx","expires_in":86400}
     * @param int $timeOut
     * @return bool|string
     */
    private function getMerchantToken($timeOut = 6)
    {
        try{
            $token = Cache::store('redis')->get('nuonuo_token');
            if (!$token){
                $headers = ["Content-Type: application/x-www-form-urlencoded"];
                $params = ["client_id" => $this->APPKey,"client_secret" => $this->APPSecret,"grant_type" => "client_credentials"];
                $params = http_build_query($params);
                $res = $this->postCurl($this->AUTH_URL,$params,$headers, $timeOut);
                $res_arr = json_decode($res,true);
                $this->accessToken = $res_arr['access_token'];
                Cache::store('redis')->set('nuonuo_token',$res_arr['access_token'],$res_arr['expires_in']);
            }else{
                $this->accessToken = $token;
            }
        }catch (Exception $e){
            return false;
        }
    }

    /**
     * 以post方式发起http调用
     *
     * @param string $url  url
     * @param array $params post参数
     * @param int $second   url执行超时时间，默认30s
     * @throws NnuoException
     */
    private function postCurl($url, $params, $headers = array(), $second = 30)
    {
        $ch = curl_init();
        $curlVersion = curl_version();

        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);

        if (!empty($headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }

        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_error($ch);
            curl_close($ch);
            throw new Exception("curl出错:$error");
        }
    }

    /**
     * 发送HTTP POST请求 <同步>
     * @param url       请求地址
     * @param token     授权码
     * @param taxnum    税号, 普通商户可不填
     * @param method    API名称
     * @param content   私有参数, 标准JSON格式
     * @throws NnuoException
     * @return 成功时返回，其他抛异常
     */
    private function sendPostSyncRequest($url,$taxnum = '', $method, $content, $timeOut = 6)
    {
        try {
            $senid = uniqid();
            $timestamp = time();
            $nonce = rand(10000, 1000000000);
            $finalUrl = "{$url}?senid={$senid}&nonce={$nonce}&timestamp={$timestamp}&appkey={$this->APPKey}";
            $urlInfo = parse_url($url);
            if ($urlInfo === FALSE) {
                throw new Exception("url解析失败");
            }
            $sign = self::makeSign($urlInfo["path"], $senid, $nonce, $content, $timestamp);
            $headers = array(
                "Content-Type: application/json",
                "X-Nuonuo-Sign: {$sign}",
                "accessToken: {$this->accessToken}",
                "userTax: {$taxnum}",
                "method: {$method}",
                "sdkVer: " . $this->VERSION
            );
            // 调用开放平台API
            return self::postCurl($finalUrl, $content, $headers, $timeOut);
        } catch (Exception $e) {
            throw new Exception("发送HTTP请求异常:" . $e->getMessage());
        }
    }

    /**
     * 计算签名
     *
     * @param path       请求地址
     * @param senid     流水号
     * @param appKey    appKey
     * @param appSecret appSecret
     * @param nonce     随机码
     * @param body    请求包体
     * @param timestamp    时间戳
     * @return 返回签名
     */
    private function MakeSign($path, $senid, $nonce, $body, $timestamp)
    {
        $pieces = explode('/', $path);
        $signStr = "a={$pieces[3]}&l={$pieces[2]}&p={$pieces[1]}&k={$this->APPKey}&i={$senid}&n={$nonce}&t={$timestamp}&f={$body}";
        $sign = base64_encode(hash_hmac("sha1", $signStr, $this->APPSecret, true));
        return $sign;
    }

    public function queryInvoiceList($parmas)
    {
        try{
            return $this->sendPostSyncRequest($this->URL,$this->Taxnum,'nuonuo.OpeMplatform.queryInvoiceList',json_encode($parmas));
        }catch (Exception $e){

        }
    }


    /**
     * 诺税通saas请求开具发票接口
     * @param $parmas
     * @return array|mixed
     */
    public function requestBillingNew($parmas)
    {
        try{
            $res = $this->sendPostSyncRequest($this->URL,$this->Taxnum,'nuonuo.OpeMplatform.requestBillingNew',json_encode($parmas));
            Log::info('开票返回内容');
            Log::info($res);
            $arr = json_decode($res,true);
            return $arr;
        }catch (Exception $e){
            return ['code'=>0];
        }
    }


    /**
     * (诺税通saas红字确认单申请接口
     * @param $parmas
     * @return array|mixed
     */
    public function saveInvoiceRedConfirm($parmas)
    {
        Log::info("诺税通saas红字确认单申请接口");
        Log::info($parmas);
        try{
            $res = $this->sendPostSyncRequest($this->URL,$this->Taxnum,'nuonuo.OpeMplatform.saveInvoiceRedConfirm',json_encode($parmas));
            Log::info('红冲确认申请返回内容');
            Log::info($res);
            $arr = json_decode($res,true);
            return $arr;
        }catch (Exception $e){
            return ['code'=>0];
        }
    }

    /**
     * (诺税通saas红字确认单查询接口
     * @param $parmas
     * @return array|mixed
     */
    public function queryInvoiceRedConfirm($parmas)
    {
        try{
            $res = $this->sendPostSyncRequest($this->URL,$this->Taxnum,'nuonuo.OpeMplatform.queryInvoiceRedConfirm',json_encode($parmas));
            Log::info('红字确认单查询返回内容');
            Log::info($res);
            $arr = json_decode($res,true);
            return $arr;
        }catch (Exception $e){
            return ['code'=>0];
        }
    }


    /**
     * 诺税通saas发票快捷冲红接口
     * @param $parmas
     * @return array|mixed
     */
    public function fastInvoiceRed($parmas)
    {
        try{
            $res = $this->sendPostSyncRequest($this->URL,$this->Taxnum,'nuonuo.OpeMplatform.fastInvoiceRed',json_encode($parmas));
            Log::info('快捷冲红接口返回内容');
            Log::info($res);
            $arr = json_decode($res,true);
            return $arr;
        }catch (Exception $e){
            return ['code'=>0];
        }
    }


    /**
     * 诺税通saas发票详情查询接口
     * @param $parmas
     * @return array|mixed
     */
    public function queryInvoiceResult($parmas)
    {
        try{
            $res = $this->sendPostSyncRequest($this->URL,$this->Taxnum,'nuonuo.OpeMplatform.queryInvoiceResult',json_encode($parmas));
            Log::info('发票详情查询接口返回内容');
            Log::info($res);
            $arr = json_decode($res,true);
            return $arr;
        }catch (Exception $e){
            return ['code'=>0];
        }
    }
}
